using System;
using Collections = System.Collections.Generic;
using Text = System.Text;
using NU.Separacion;
using System.Collections.Generic;

namespace NU.Analisis
{
	public class Analizador
	{
		private int indice;
		private IList<object> Muestras;
		private readonly Instruccion resultado;

		public Analizador(Collections.IList<object> muestras)
		{
			Muestras = muestras;
			indice = 0;
			resultado = ParseStmt();

			if (indice != Muestras.Count)
				throw new Exception("expected EOF");
		}

		public Instruccion Resultado
		{
			get { return resultado; }
		}

		private Instruccion ParseStmt()
		{
			Instruccion result;

			if (indice == Muestras.Count)
				throw new Exception("expected statement, got EOF");

			if (Muestras[indice].Equals("var"))
				result = AnalizarVar ();
			else if (Muestras[indice].Equals("para"))
				result = AnalizarPara ();
			else if (Muestras[indice] is string)
				result = AnalizarIdentificador ();
			else
				throw new Exception("parse error at token " + indice + ": " + Muestras[indice]);

			if (indice < Muestras.Count && Muestras[indice].Equals(Operador.PutoYComa))
			{
				indice++;
				if (indice < Muestras.Count && !Muestras[indice].Equals("fin"))
				{
					Secuencia sequence = new Secuencia();
					sequence.Primero = result;
					sequence.Segundo = ParseStmt();
					result = sequence;
				}
			}

			return result;
		}


		private Instruccion AnalizarVar(){
			indice++;
			Declaracion declarar = new Declaracion();

			if (indice < Muestras.Count && Muestras[indice] is string)
				declarar.Identificador = (string)Muestras[indice];
			else
				throw new Exception("expected variable name after 'var'");

			indice++;

			if (indice == Muestras.Count || !Muestras[indice].Equals( Operador.Igual))
				throw new Exception("expected = after 'var ident'");

			indice++;

			declarar.Expresion = AnalizarExpresion();
			return declarar;
		}

		private Instruccion AnalizarIdentificador(){
			string identificador = Muestras [indice++].ToString ();
			Instruccion inst = null;

			if (indice == Muestras.Count)
				throw new Exception("expected '='");

			if (Muestras[indice].Equals( Operador.Igual))
			{
				Asignacion assign = new Asignacion();
				assign.Identificador = identificador;
				indice++;
				assign.Expresion = AnalizarExpresion();
				inst = assign;

			} 
			else if (Muestras[indice].Equals( Operador.ParentesisIzquierdo))
			{
				Invocacion inv = new Invocacion();
				inv.Identificador = identificador;
				indice++;

				if(!Muestras[indice].Equals(Operador.ParentesisDerecho))
					inv.Expresion = AnalizarExpresion();

				if (!Muestras[indice++].Equals( Operador.ParentesisDerecho))
					throw new Exception("se esperaba ')'");

				inst = inv;
			} else 
				throw new Exception("expected '='");

			return inst;
		}

		private Instruccion AnalizarPara(){
			indice++;
			CicloPara cicloPara = new CicloPara();

			if (indice < Muestras.Count && Muestras[indice] is string)
				cicloPara.Identificador = (string)Muestras[indice];
			else
				throw new Exception("expected identifier after 'for'");

			indice++;
			if (indice == Muestras.Count || !Muestras[indice].Equals(Operador.Igual))
				throw new Exception("for missing '='");

			indice++;
			cicloPara.Desde = AnalizarExpresion();

			if (indice == Muestras.Count || !Muestras[indice].Equals("hasta"))
				throw new Exception("expected 'to' after for");

			indice++;
			cicloPara.Hasta = AnalizarExpresion();

			if (indice == Muestras.Count || !Muestras[indice].Equals("hacer"))
				throw new Exception("expected 'do' after from expression in for loop");

			indice++;
			cicloPara.Cuerpo = ParseStmt();


			if (indice == Muestras.Count || !Muestras[indice].Equals("fin"))
				throw new Exception("unterminated 'for' loop body");

			indice++;
			return cicloPara;
		}


		private Expresion AnalizarExpresion()
		{
			if (indice == Muestras.Count)
			{
				throw new Exception("expected expression, got EOF");
			}

			if (Muestras[indice] is Text.StringBuilder)
			{
				string value = ((Text.StringBuilder)Muestras[indice++]).ToString();
				LiteralCadena stringLiteral = new LiteralCadena();
				stringLiteral.Valor = value;
				return stringLiteral;
			}
			else if (Muestras[indice] is int)
			{
				int intValue = (int)Muestras[indice++];
				LiteralEntero intLiteral = new LiteralEntero();
				intLiteral.Valor = intValue;
				return intLiteral;
			}
			else if (Muestras[indice] is string)
			{
				string ident = (string)Muestras[indice++];
				Variable var = new Variable();
				var.Identificador = ident;
				return var;
			}
			else
			{
				throw new Exception("expected string literal, int literal, or variable");
			}
		}
	}
}

